<?php
/*
 * Controller
 *
 *
 * @version 1.0
 * @license  GNU GPL
 * @author Artur Kmera <artur.kmera@arrowplatform.org>
 */
class ArrowController extends ArrowObject{		
	
	private static $scriptEndOk = false ;
	
	/**
	 * Current project
	 *
	 * @var ArrowProject
	 */
	private $project = null;

	/**
	 * Auth handler
	 *
	 * @var IAuthHandler
	 */
	private $authHandler;

	/**
	 * Session handler
	 *
	 * @var ISessionHandler
	 */
	private $sessionHandler;
	
	/**
     * AccessHandler
     * @var unknown_type
     */
	private $accessHandler;
	

	/**
	 * Self instance
	 * @var Controller
	 */
	private static $oInstance = null;

	// ToDo: dlaczego są dwie zmienne
	private static $loadedModels = array();
	private static $loadedModelsPaths = array();


	public static function setLoadedModels( $loadedModels ){
		self::$loadedModels = $loadedModels;
	}
	
	/**
	 * Returns controller default instance
	 *
	 * @return ArrowController
	 */
	public static function getDefault(  /*ArrowRouter $router = null*/ ){
		if( self::$oInstance == null ){
			self::$oInstance = new ArrowController( );
		}
		return self::$oInstance;
	}

	private function __construct( ){
		ArrowLogger::log("[ArrowController] Setting default project and handlers");
		ArrowErrorHandler::getDefault();
	}
	
	public function getProject() {
		if( $this->project == null ) {
			require_once( ARROW_COMPONENTS_PATH."/project/ArrowProject.php" );
			$this->project = new ArrowProject( ) ; 
			$this->sessionHandler = $this->project->getHandler(ArrowProject::ISessionHandler);
			$this->authHandler = $this->project->getHandler(ArrowProject::IAuthHandler);
			$this->accessHandler = $this->project->getHandler(ArrowProject::IAccessHandler);
			$this->project->getHandler(ArrowProject::IExceptionHandler);
			$emode =  $this->project->getErrorDisplayLevel() ; 
			ArrowErrorHandler::getDefault()->setLevel( $emode );
		}
		return $this->project;
	}
	
	public function getAuthHandler() { return $this->authHandler ; }
	
	public function getAccessHandler() { return $this->accessHandler ; }
	
	public function getSessionHandler() { return $this->sessionHandler ; }

	public static function importModel( $modelName ){
		if( array_search( $modelName, self::$loadedModels ) === false ){
			ArrowLogger::log("[ArrowController] Importing model: ".$modelName);
			self::$loadedModels[] = $modelName;
			self::loadFile("model", $modelName);
		}
	}

	private static function loadFile( $type, $name ){
		switch( $type ){
			case "model":
				$path = ArrowController::getModelPath($name);
				require_once $path;
				self::$loadedModelsPaths[] = $path;
				break;
		}
	}

	public static function getModelPath( $modelName ){
		$modelName = str_replace( "..", "", $modelName ) ;   // zabezpioeczenie przeciw poruszaniu się po katalogach
		
		$path = "/".str_replace( ".", "/", $modelName ).".php";
	
		// TODO: sprawdzanie na podstawie konfiguracji
		if( is_readable(ARROW_COMPONENTS_PATH.$path) ){

			return ARROW_COMPONENTS_PATH.$path;

		}elseif( is_readable( "/application/components".$path ) ){

			return  "/application/models".$path;

		}
		
		print_r( $modelName);
		exit;
		
		$exception =  array( "src" => "ArrowController", "msg" => "Model not found", "model" => $modelName, "findPath" => $path );
		throw new ArrowCoreException($exception);
		
	}

	/**
	 * Returns model instance
	 *
	 * @param String $model
	 * @return Object
	 * TODO check if model can be instanciated (implements getDefault)
	 */
	public static function getModelInstance($model){

		ArrowLogger::log("[ArrowController] Getting model instance: ".$model);

		self::importModel($model);
		$tmp = explode( ".", $model );
		$modelName = array_pop($tmp);
		$instance = call_user_func(array($modelName, "getDefault"));
		return $instance;
	}

	/**
	 * Returns bean instance
	 *
	 * @param String $beanId
	 * @return Object
	 */
	public function getBeanInstance( $beanId ){
		if(class_exists("Bean".$beanId)){
			$class = "Bean".$beanId;
			return new $class;
		}
		$descriptor = $this->project->getBeansStructure()->getBeanById($beanId);
		return $descriptor->getBeanInstance();
	}

	/**
	 * Excutes given bean
	 *
	 * @param ArrowBeanDescriptor $beanDescriptor
	 * @return mixed
	 */
	public function executeBean( ArrowBeanDescriptor $beanDescriptor ){

		ArrowLogger::log("[ArrowController] Executing logic bean: ".$beanDescriptor->toString());
		/*Check if bean can be used by user*/
		$object = $beanDescriptor;
		$this->accessHandler->isBeanAccessible( $beanDescriptor, "execute" ) ;
		/*Execute bean*/
		return $beanDescriptor->execute(ArrowRequestContext::getDefault());

	}

	public function remoteResponse( ArrowBeanDescriptor $beanDescriptor ){
		ArrowLogger::log("[ArrowController] Sending remote response from: ".$beanDescriptor->toString());
		//ArrowLogger::log( "[ArrowController] Remote call, loading response handler..." );
		$remoteResponseHandler = $this->project->getHandler(ArrowProject::IRemoteResponseHandler);
		$remoteResponseHandler->setHeaders();
		//ArrowLogger::log( "[ArrowController] Remote call, calling bean and returning response" );
		$response = $remoteResponseHandler->getResponse($this->executeBean( $beanDescriptor));
		self::end($response);
	}


	/**
	 * redirects to next template after bean has been executed
	 *
	 */
	public function processToView(){
		ArrowLogger::log("[ArrowController] Redirecting to View after bean execucion");

		$request = ArrowRequestContext::getDefault();
		$input = array_merge($request->getGet(), $request->getPostToGet(), $request->getRegistredUrlVars());

		$link = ArrowRouter::$INDEX_FILE."?";

		foreach( $input as $name => $val ){
			if( $name != ArrowRouter::ACTION_BEAN_PARAMETER &&  $name != ArrowRouter::MEDIATOR_PARAMETER )
			$link.= $name."=".$val."&";
		}
		$link = substr($link,0,-1); //delete last &
		header( "Location: ".$link );
		self::end();
	}

	/**
	 * redirects to next template
	 *
	 */
	public static function redirectToView( ArrowTemplateDescriptor $template ){
		ArrowLogger::log("[ArrowController] Redirecting to View after bean execucion");
		$link = ArrowRouter::$INDEX_FILE."?".ArrowRouter::TEMPLATE_PARAMETER."=".$template->getId();
		header( "Location: ".$link );
		self::end();
	}

	/**
	 * redirect to rewrite (static) address with optional parameters (query string)
	 *
	 */
	public static function redirectToStaticAddress( $address, $addVars = true ){
		ArrowLogger::log("[ArrowController] Redirecting to static address: ".$address);

		$request = ArrowRequestContext::getDefault();
		$input = array_merge($request->getPostToGet(), $request->getRegistredUrlVars());

		$link = $address;

		if($addVars){
			if( strpos( $link, "?" ) === false )
			$link.="?";
			else
			$link.="&";

			foreach( $input as $name => $val ){
				if( $name != ArrowRouter::ACTION_BEAN_PARAMETER &&  $name != ArrowRouter::MEDIATOR_PARAMETER && $name != ArrowRouter::TEMPLATE_PARAMETER  )
				$link.= $name."=".$val."&";
			}
			$link = substr($link,0,-1); //delete last &
		}
		
		
		header( "Location: ".$link );
		//exit($link);
		self::end(0);
	}

	public static function redirectToTemplate( $template ) {
		$template_descriptor = ArrowController::getDefault()->getProject()->getTemplatesStructure( )->getTemplateByMappingPath( $template );		
		ArrowController::redirectToView( $template_descriptor );
	}
	
	/**
	 * Rollback request (eg for mediators)
	 *
	 */
	public function rollbackRequest(  ){
		ArrowLogger::log("[ArrowController] Rolling back the request");
		$request = ArrowRequestContext::getDefault();
		$input =  $request->getRegistredUrlVars();
		$link = "";

		foreach( $input as $name => $val ){
			$link.= "&".$name."=".$val;
		}
		
		// TODO: sprawdzać czy referef istnieje !!!!!!
		
		header("Location: ".$_SERVER["HTTP_REFERER"].$link);
		self::end();
	}

	/**
	 * allow all additional modules (logger, db) to close connections and finish execution
	 *
	 */
	public static function end( $response = "" ){
		ArrowCacheProvider::generateModelsCache(self::$loadedModels,ArrowController::$loadedModelsPaths);
		self::$scriptEndOk = true;
		@exit($response);
	}
}
?>